package own.stu.jobgib.playown.juc.threadLocal;

//import com.alibaba.ttl.TransmittableThreadLocal;
//import com.alibaba.ttl.TtlRunnable;

import com.alibaba.ttl.TransmittableThreadLocal;
//import com.alibaba.ttl3.TransmittableThreadLocal;
//import com.alibaba.ttl3.TtlRunnable;
import com.alibaba.ttl.TtlRunnable;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@Slf4j
public class TTLTest {
    public static void main(String[] args) throws Exception {

//        hashCodeTest();
//        testInheritThreadLocal();
//        testInheritThreadLocalLoop();

//        testTransmittableThreadLocalLoop();
        testRunnable();
    }

    private static void hashCodeTest() {
        Data lio = new Data("lio", 1);
        log.info("hashcode is {}", lio.hashCode());

        lio.setId(2);
        log.info("hashcode is {}", lio.hashCode());

        class D {
            Integer id = 2;

            public void setId(Integer id) {
                this.id = id;
            }
        }

        D dV = new D();
        log.info("hashcode is {}", dV.hashCode());

        dV.setId(3);
        log.info("hashcode is {}", dV.hashCode());
    }

    /*
    可以从结果看出 inheritThreadLocal 在父子线程中的表现，
    如果传递的对象（引用类型）会被修改，且没有做深拷贝（如直接传递引用或是浅拷贝），那么
        1. 因为跨线程传递而不再有线程封闭，传递对象在多个线程之间是有共享的。
        2. 与JDK的[InheritableThreadLocal.childValue()](https://docs.oracle.com/javase/10/docs/api/java/lang/InheritableThreadLocal.html#childValue(T))一样，
        需要使用者/业务逻辑注意保证传递对象的线程安全。
     */
    private static void testInheritThreadLocal() throws ExecutionException, InterruptedException {
        InheritableThreadLocal<Data> local = new InheritableThreadLocal<>();
        Data lio = new Data("lio", 1);
        local.set(lio);
//        log.info("data: {}, {}", lio, lio.hashCode());

        // hashcode 不需要看，这里lombok 进行了重写，看不出对象的地址信息
        log.info("Before -> t: {}, v: {}, {}", Thread.currentThread().getName(), local.get(), local.get().hashCode());

        Thread thread = new Thread(() -> {
            // log.info("{} parent thread is {}", Thread.currentThread().getName(), Thread.currentThread().hashCode());
            log.info("t: {}, v: {}, {}", Thread.currentThread().getName(), local.get(), local.get().hashCode());
            Data data = local.get();
            data.setId(2);
        });
        thread.start();
        thread.join();

        log.info("After -> t: {}, v: {}, {}", Thread.currentThread().getName(), local.get(), local.get().hashCode());
    }

    private static void testInheritThreadLocalLoop() throws InterruptedException {
        InheritableThreadLocal<Data> local = new InheritableThreadLocal<>();
        testInheritThreadLocalLoop(local);
    }

    private static void testTransmittableThreadLocalLoop() throws InterruptedException {
        InheritableThreadLocal<Data> local = new InheritableThreadLocal<>();
        testInheritThreadLocalLoop(local);
    }


    private static void testInheritThreadLocalLoop(InheritableThreadLocal<Data> local) throws InterruptedException {

        Data lio = new Data("lio", 1);
        local.set(lio);

        log.info("Before -> t: {}, v: {}", Thread.currentThread().getName(), local.get());

        looped(local, 3);

        log.info("After -> t: {}, v: {}", Thread.currentThread().getName(), local.get());
    }


    private static void looped(InheritableThreadLocal<Data> local, int depth) throws InterruptedException {

        if (depth == 0) {
            return;
        }

        Thread thread = new Thread(runnable(local, depth), "t - " + depth);
        thread.start();
        thread.join();
    }

    private static Runnable runnable(InheritableThreadLocal<Data> local, int depth) {
        return () -> {
            log.info("Before -> t: {}, v: {}", Thread.currentThread().getName(), local.get());
            Data data = local.get();
            data.setId(2 + depth);

            try {
                looped(local, depth - 1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            log.info("After -> t: {}, v: {}", Thread.currentThread().getName(), local.get());
        };
    }

    /*
    注意hashCode的问题，lombok 没有根据地址 计算hashcode
     */
    @lombok.Data
    @AllArgsConstructor
    static class Data {
        String name;
        Integer id;
    }

    private static void testRunnable() throws Exception {
        TransmittableThreadLocal<Data> threadLocal = new TransmittableThreadLocal<>();
        threadLocal.set(new Data("lio", 1));

        log.info("Before -> t: {}, v: {}", Thread.currentThread().getName(), threadLocal.get());

        TtlRunnable ttlRunnable = TtlRunnable.get(() -> {
            log.info("child thread - {} get {}", Thread.currentThread().getName(), threadLocal.get());
            threadLocal.get().setId(2);
        });

        ExecutorService service = Executors.newFixedThreadPool(4);
        service.submit(ttlRunnable).get();

        threadLocal.set(new Data("diva", 44));

        /*ttlRunnable = TtlRunnable.get(() -> {
            log.info("child thread - {} get {}", Thread.currentThread().getName(), threadLocal.get());
//            threadLocal.get().setId(2);
        });*/
        service.submit(ttlRunnable).get();

        log.info("After -> t: {}, v: {}", Thread.currentThread().getName(), threadLocal.get());

        service.shutdown();
    }
}
